Esthefanie Pessoa Lanza - 2015112736
João Antônio Dornelas Orlando Netto - 2014103407
Avaliação experimental: A implementação da solução proposta foi interpretada com Python 3.5. Os experimentos foram executados em um sistema com processador Intel Core I5 7400, 8GB de memória RAM e GPU NVIDIA Geforce GTX 1060 6GB.
1 - Abrir o Anaconda Prompt
2 - Criar um novo environment no Anaconda: conda create -n tensorflow python=3.5
3 - Ativar o tensorflow: activate tensorflow
4 - Instalar e atualizar o tensorflow:
Importação das bibliotecas necessárias para o projeto
import sys
import cv2
import numpy
import os
from pathlib import Path
from matplotlib import pyplot as plt
from IPython.display import clear_output
%matplotlib inline
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.layers.convolutional import Conv2D, MaxPooling2D
from keras.utils import np_utils
Definição das funções de processamento e impressão das imagens
#Le a imagem, converte para tons de cinza e corta no padrão correto
def correctImage(imagePath):
img = cv2.imread(imagePath);
img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
img = img[13:41, 12:]
return img
#Divide a imagem em N partições iguais
def slice_horizontaly(img , slicesNum):
imgArray = []
height, width = img.shape[:2]
sliceSize = int(width / slicesNum)
sliceStart = 0
for i in range(0,slicesNum):
newSlice = img[:,sliceStart:sliceStart+sliceSize]
imgArray.append(newSlice)
sliceStart += sliceSize
return imgArray
#Imprime a imagem no padrão BGR
def printImageColor( img ):
rgbImg = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
plt.imshow(rgbImg, cmap = 'gray')
plt.axis("off")
plt.xticks([])
plt.yticks([])
plt.show()
return
#Imprime a imagem em tons de cinza
def printImage( img ):
plt.imshow(img, cmap = 'gray')
plt.axis("off")
plt.xticks([])
plt.yticks([])
plt.show()
return
Definição das funções para tratamento do output
#Recebe um valor e retorna o array binário categorizado correspondente
def binaryCategory(index, setSize):
binaryArray = [0] * setSize
binaryArray[index] = 1
return binaryArray
#Para cada elemento de um vetor de entrada, cria um vetor binário categorizado
def createCategorical(inputArray, inputList):
outputArray = []
for i in inputArray:
outputArray.append(binaryCategory(inputList.index(i),37))
return outputArray
Definição das funções de processamento dos datasets
#Trata o captcha atual e adiciona suas partições no vetor de saída
def addCaptcha(imageDirectory, textDirectory, imageFile, textFile, inputArray, outputArray):
fileContent = open(textDirectory + "/" + textFile, "r").read()
inputImage = correctImage(imageDirectory + "/" + imageFile)
imageSlices = slice_horizontaly(inputImage, 6)
for i in range(len(imageSlices)):
inputArray.append(imageSlices[i])
outputArray.append(fileContent[i])
#Cria um novo dataset, recebendo como parâmetro de entrada o diretório de imagens e o
#diretório contendo a classificação de cada imagem
def createDataset(imageDirectory, textDirectory):
inputArray, outputArray = [], []
directory = Path(imageDirectory)
fileList = [f for f in directory.glob('**/*') if f.is_file()]
for f in range(0,len(fileList)):
imageFile = str(fileList[f]).split("\\")[-1]
clear_output(wait=True)
print(str(f+1) + "/" + str(len(fileList)))
textFile = imageFile.replace(".jpg", ".txt")
addCaptcha(imageDirectory, textDirectory, imageFile, textFile, inputArray, outputArray)
return numpy.array(inputArray), numpy.array(outputArray)
Definição dos diretórios de treino, validação e teste.
#Caminho dos diretórios que serão usados pela rede
train_directory = 'dados/CAPTCHA-10k/treinamento'
validation_directory = 'dados/CAPTCHA-10k/validacao'
test_directory = 'dados/CAPTCHA-10k/teste'
labels = 'dados/CAPTCHA-10k/labels10k'
Criação do dataset de entrada, usando as imagens do diretório de treino. Para cada imagem existente no diretório, dividimos a mesma em seis partes iguais e o mesmo foi realizado para o arquivo de texto. Dessa forma, para cada posição do vetor de entradas, temos a imagem de uma letra e o caractere correspondente.
#Cria um novo dataset, contendo as imagens de entrada e de saída
inputArray, outputArray = createDataset(train_directory, labels)
#Imprime um exemplo de valor lido
print("Imagem exemplo: ")
printImage(inputArray[0])
print("Letra correspondente:" + outputArray[0])
#Normaliza os dados lidos
inputArray = inputArray.reshape(inputArray.shape[0], 28, 28, 1)
inputArray = inputArray.astype('float32')/255
Para cada caractere do vetor de saída, convertemos seu valor para um vetor binário de 37 posições. O índice de cada posição com o valor 1 corresponde ao caractere especificado no vetor inputList.
#Lista usada para categorização binária de cada saída
inputList = ['0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','?']
print("Letra correspondente: " + outputArray[0])
#Converte a saída em um vetor categórico binário
outputArray = createCategorical(outputArray, inputList)
print("Vetor binário:")
print(outputArray[0])
Construção da rede neural
#Cria camada sequencial
model = Sequential()
#Cria a camada de entrada
model.add(Conv2D(256, (3, 3), strides=(1, 1), activation='relu', padding='same', input_shape=(28,28,1)))
#Cria as camadas ocultas
model.add(Conv2D(256, (3, 3), padding='same', activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2), padding='same'))
model.add(Dropout(0.25))
#Cria a camada totalmente conectada
model.add(Flatten())
model.add(Dense(256, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(37, activation='softmax'))
#Imprime um sumário da rede construída
model.summary()
Criação do dataset de validação, utilizando os mesmos procedimentos do dataset de treinamento.
#Cria um novo dataset utilizando o diretório com as imagens e validação
inputValidacao, outputValidacao = createDataset(validation_directory, labels)
#Normaliza o dataset criado
shapedinputValidacao = inputValidacao.reshape(inputValidacao.shape[0], 28, 28, 1)
shapedinputValidacao = shapedinputValidacao.astype('float32')/255
#Cria a categorização binária para cada valor da saída
outputValidacao = createCategorical(outputValidacao,inputList)
Compilação do modelo e treinamento da rede, usando o dataset de treino. Nesse passo, também é utilizado o dataset de validação para validar os dados.
#Compila o modelo
model.compile(loss='categorical_crossentropy',
optimizer='adam',
metrics=['accuracy'])
#Executa a rede, com um batch size de 50, executando em 10 epocas, e validando os dados com o dataset de validação
response = model.fit(inputArray, outputArray,
batch_size=50, epochs=10, verbose=1, validation_data=(shapedinputValidacao, outputValidacao))
Criação do dataset de testes, utilizando os mesmos procedimentos do dataset de treinamento e validação.
#Cria os datasets com os arquivos do diretório de testes
inputTeste, outputTeste = createDataset(test_directory, labels)
#Normaliza os dados do diretório de testes
shapedinputTeste = inputTeste.reshape(inputTeste.shape[0], 28, 28, 1)
shapedinputTeste = shapedinputTeste.astype('float32')/255
#Converte as saídas para categorização binária
outputTeste = createCategorical(outputTeste,inputList)
Avaliação do modelo já treinado em relação ao dataset de teste (por caractere)
#Avalia a acurácia do modelo, com o dataset de teste
score = model.evaluate(shapedinputTeste, outputTeste, verbose=1)
print(score)
Encontramos o primeiro captcha no diretório de teste especificado
#Encontra o primeiro captcha no diretório especificado
firstCaptchaFile = (os.listdir(test_directory)[0])
currentCaptcha = int(firstCaptchaFile.split(".")[0])
print("Iniciamos a validação pelo CAPTCHA de número: " + str(currentCaptcha))
Para cada captcha, identifica-se quantos caracteres foram assimilados corretamente, e gera-se um vetor de saída contendo quantos captchas acertaram um número N de caracteres.
arrayRightAnswers = [0] * 7
i = 0
# Para todos os caracteres da validação
while(i < len(inputTeste)-5):
# Limpamos as respostas para os acertos e o previsto para o captcha anterior
rightAnswer = 0
predictedCaptcha = ''
# Para cada CAPTCHA
for charCAPTCHA in range(0,6):
# Encontra resposta correta para aquele carctere
currentIndex = i + charCAPTCHA
shapedOutput = outputTeste[currentIndex:currentIndex+1]
rightOutput = numpy.argmax(shapedOutput)
# Trata input
shapedInputTeste = inputTeste[currentIndex:currentIndex+1,:,:]
shapedInputTeste = shapedInputTeste.reshape(shapedInputTeste.shape[0], 28, 28, 1)
shapedInputTeste = shapedInputTeste.astype('float32')/255
# Calcula ouput previsto
predictedOutput = model.predict(shapedInputTeste, batch_size=None, verbose=0)
predictedOutput = numpy.argmax(predictedOutput)
predictedCaptcha += inputList[predictedOutput]
# Verificamos se a rede acertou a resposta
if (predictedOutput == rightOutput):
rightAnswer += 1
# Imprimimos o captcha com a resposta
captchaName = str(currentCaptcha)
while(len(captchaName) < 6): captchaName = "0" + captchaName
inputImage = cv2.imread(test_directory + "/" + captchaName + ".jpg")
printImage(inputImage)
print(predictedCaptcha)
# Adicionamos o número de acertos do captcha ao vetor de acertos
arrayRightAnswers[rightAnswer] += 1
# Passamos para o próximo captcha
i += 6
currentCaptcha += 1
Calcula a probabilidade de acerto de N caracteres por captcha, onde N é índice do vetor.
#Imprime previsões corretas
print("Número de caracteres previstos corretamente: ")
print(arrayRightAnswers)
print("\n")
percentMin = [0] * 7
for i in range(0,len(arrayRightAnswers)):
for n in range(i,len(arrayRightAnswers)):
percentMin[i] += arrayRightAnswers[n]
percentMin[i] = percentMin[i]/(len(inputTeste)/6)
#Imprime probabilidade de acertos
print("Probabilidade de acerto de no mínimo N caracteres: ")
print(percentMin)
Imprime o gráfico com o número de acertos mínimos por captcha
plt.plot(percentMin)
plt.title("Resultado")
plt.grid(True)
plt.ylim([0,1.1])
plt.xlim([0,6])
plt.ylabel("Taxa de reconhecimento")
plt.xlabel("Número mínimo de caracteres reconhecidos por captcha")
plt.show()